package cn.txauction.business.dao.base;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.NonUniqueResultException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import cn.txauction.common.exception.TxRuntimeException;
import cn.txauction.web.paging.PagingResult;

@SuppressWarnings("unchecked")
public class BaseDao<T> extends HibernateDaoSupport implements IBaseDao<T> {

	protected static Log log = LogFactory.getLog(BaseDao.class);

	private final Class<T> clazz;
	private final String entityName;

	@Resource(name = "hibernateTemplate")
	public final void setHibernateTemplate_0(HibernateTemplate hibernateTemplate) {
		super.setHibernateTemplate(hibernateTemplate);
	}

	public void flushAndClearSession() {
		// FIXME 通过getCurrentSession()可以得到相应的session吗?
		Session session = getHibernateTemplate().getSessionFactory().getCurrentSession();
		session.flush();
		session.clear();
	}

	public BaseDao() {
		Type type = this.getClass().getGenericSuperclass();
		if (!(type instanceof ParameterizedType)) {
			throw new TxRuntimeException("没有传递类型参数T");
		}

		ParameterizedType pt = (ParameterizedType) type;
		this.clazz = (Class<T>) pt.getActualTypeArguments()[0];
		this.entityName = this.clazz.getName();
	}

	public long save(T t) {
		return (Long) getHibernateTemplate().save(getEntityName(), t);
	}

	// TODO use hql: where id in (?) . and set parameter(0, idsList)??
	public void delete(int... ids) {
		if (ids == null || ids.length == 0) {
			return;
		}

		for (int id : ids) {
			Object obj = getHibernateTemplate().get(getEntityName(), id);
			if (obj != null) {
				getHibernateTemplate().delete(getEntityName(), obj);
			}
		}
	}

	public void delete(T t) {
		getHibernateTemplate().delete(getEntityName(), t);
	}

	public void update(T t) {
		getHibernateTemplate().update(getEntityName(), t);
	}
	public void merge(T t) {
		getHibernateTemplate().merge(getEntityName(), t);
	}

	public void saveOrUpdate(T t) {
		getHibernateTemplate().saveOrUpdate(getEntityName(), t);
	}

	public T get(Long id) {
		return (T) getHibernateTemplate().get(getEntityName(), id);
	}

	public List<T> findAll() {
		String queryString = "from " + getEntityName();
		return getHibernateTemplate().find(queryString);
	}

	public long getTotal() {
		return findIntResult("select count(o) from " + getEntityName() + " o", null);
	}

	public PagingResult<T> findPagingResult(int firstResult, int maxResult) {
		String queryString = "select count(o) from " + getEntityName() + " o";
		long total = findIntResult(queryString, new Object[] {});

		DetachedCriteria dc = DetachedCriteria.forEntityName(getEntityName());
		List<T> items = getHibernateTemplate().findByCriteria(dc, firstResult, maxResult);

		return new PagingResult<T>(items, total);
	}
	
	public PagingResult<T> findPagingResult(String queryString, int firstResult, int maxResult) {
		return findPagingResult(queryString, null, firstResult, maxResult);
	}
	
	public PagingResult<T> findPagingResult(final String queryString,final Object[] params, final int firstResult, final int maxResult) {
		String countString = "select count(o) from (" + queryString + ") o";
		long total = findIntResult(countString, new Object[] {});

		List<T> items = getHibernateTemplate().executeFind(new HibernateCallback(){
			public Object doInHibernate(Session session)
					throws HibernateException, SQLException {
				Query query = session.createQuery(queryString);
				if(params != null && params.length > 0){
					for(int i=0; i<params.length; i++){
						query.setParameter(i, params[i]);
					}
				}
				query.setFirstResult(firstResult);
				query.setMaxResults(maxResult);
				List<T> list = query.list();
				return list;
			}
		});

		return new PagingResult<T>(items, total);
	}

	/**
	 * 获取实体的名字, 默认为持久类的全限定名
	 * 
	 * @return
	 */
	protected String getEntityName() {
		return this.entityName;
	}

	/**
	 * 查询结果列表中的第一个结果,没有查询条件
	 */
	public Object findFirstResult(DetachedCriteria dc) {
		List list = getHibernateTemplate().findByCriteria(dc, 0, 1);
		return list.size() > 0 ? list.get(0) : null;
	}

	public long findIntResult(String queryString, Object value) {
		return findIntResult(queryString, new Object[] { value });
	}

	public long findIntResult(String queryString, Object[] values) {
		Object result = findUniqueResult(queryString, values);
		if (result == null) {// TODO 会为null吗？
			return 0;
		}
		return ((Number)result).intValue();
	}

	/**
	 * 根据HQL语句和单一参数返回一个domain对象
	 * @param queryString
	 * @param value 参数
	 * @return
	 */
	public T findUniqueResult(String queryString, Object value) {
		return findUniqueResult(queryString, new Object[] { value });
	}
	
	/**
	 * 根据HQL语句和参数数组返回一个唯一的domain对象，如果不唯一的话则抛出异常
	 * @param queryString
	 * @param value 参数数组
	 * @return
	 */
	public T findUniqueResult(String queryString, Object[] values) {
		// TODO 如果没有 findObject(String, Object[]), 并不报错, 而是去找 findObject(String, Object)
		// 如果没有记录，则返回一个空的List
		List<T> list = getHibernateTemplate().find(queryString, values);
		return uniqueElement(list);
	}

	/**
	 * 根据HQL语句和参数数组返回查询结果的第一个domain对象
	 * @param queryString
	 * @param values
	 * @return
	 */
	public T findFirstResult(String queryString, Object[] values) {
		// TODO 如果没有 findObject(String, Object[]), 并不报错, 而是去找 findObject(String, Object)
		// 如果没有记录，则返回一个空的List
		List<T> list = getHibernateTemplate().find(queryString, values);
		if(list.size() > 0)
			return list.get(0);
		return null;
	}
	
	protected T uniqueElement(List<T> list) throws NonUniqueResultException {
		int size = list.size();
		if (size == 0)
			return null;
		T first = list.get(0);
		for (int i = 1; i < size; i++) {
			if (list.get(i) != first) {
				throw new TxRuntimeException(new NonUniqueResultException(list.size()));
			}
		}
		return first;
	}

}
